package com.loie.xcx.syUtil;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import java.io.ByteArrayInputStream;
import java.nio.charset.Charset;
import java.security.*;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.security.interfaces.RSAPrivateKey;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;

//@Slf4j
public class RSAUtils {
    /**
     * 加密算法RSA
     */
    public static final String KEY_ALGORITHM = "RSA";
    public static final String SIGNATURE_ALGORITHM = "SHA256WithRSA";

    private static final Charset DEFAULT_CHARSET = Charset.defaultCharset();
    private static KeyFactory keyFactory;

    static {
        try {
            keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
        } catch (Exception e) {
//            LOGGER.error("加载[{}]算法失败", KEY_ALGORITHM);
        }
    }

    /**
     * <p>
     * 公钥加密
     * </p>
     *
     * @param data      源数据
     * @param publicKey 公钥
     * @return
     * @throws Exception
     */
    public static String encryptByPublicKey(String data, PublicKey publicKey) throws IllegalBlockSizeException, InvalidKeyException, BadPaddingException, NoSuchAlgorithmException, NoSuchPaddingException {
        // 对数据加密
        try {
            Cipher cipher = cipher = Cipher.getInstance(KEY_ALGORITHM);
            cipher.init(Cipher.ENCRYPT_MODE, publicKey);
            return org.apache.commons.codec.binary.Base64.encodeBase64String(cipher.doFinal(data.getBytes(DEFAULT_CHARSET)));
        } catch (Exception e) {
//            log.error("加密数据失败:" + data, e);
            throw e;
        }
    }

    /**
     * 解密
     *
     * @param
     * @param inputStr
     * @param privateKey
     * @return
     * @throws Exception
     */
    public static String decryptByRSA(String inputStr, PrivateKey privateKey) throws IllegalBlockSizeException, InvalidKeyException, BadPaddingException, NoSuchAlgorithmException, NoSuchPaddingException {
        return decryptByRSA(inputStr, privateKey, DEFAULT_CHARSET);
    }

    /**
     * 解密
     *
     * @param
     * @param inputStr
     * @param privateKey
     * @return
     * @throws Exception
     */
    public static String decryptByRSA(String inputStr, PrivateKey privateKey, Charset charset) throws NoSuchPaddingException, NoSuchAlgorithmException, BadPaddingException, IllegalBlockSizeException, InvalidKeyException {
        Cipher cipher = Cipher.getInstance(KEY_ALGORITHM);
        cipher.init(Cipher.DECRYPT_MODE, privateKey);
        byte[] output = cipher.doFinal(org.apache.commons.codec.binary.Base64.decodeBase64(inputStr));
        return new String(output, charset);
    }

    public static PrivateKey readPrivateKey(String keyContent) throws InvalidKeySpecException {
        byte[] keyBytes = Base64.getDecoder().decode(keyContent);
        PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes);
        RSAPrivateKey privateK = (RSAPrivateKey) keyFactory.generatePrivate(pkcs8KeySpec);
        return privateK;
    }

    /**
     * 获取私钥
     *
     * @param pemContent 私钥证书Base64密文
     * @return 私钥
     * //     * @throws DupException 解析异常
     */
    public static PublicKey readPublicKey(String pemContent) throws InvalidKeySpecException {
        byte[] encodeByte = Base64.getDecoder().decode(pemContent);
        PublicKey publicKey = keyFactory.generatePublic(new X509EncodedKeySpec(encodeByte));
        return publicKey;
    }

    /**
     * 用私钥对信息生成数字签名
     *
     * @param data       待签名数据
     * @param privateKey 私钥
     * @throws Exception
     */
    public static String sign(byte[] data, PrivateKey privateKey) throws NoSuchAlgorithmException, InvalidKeyException, SignatureException {
        return sign(data, privateKey, SIGNATURE_ALGORITHM);
    }

    /**
     * 用私钥对信息生成数字签名
     *
     * @param data       待签名数据
     * @param privateKey 私钥
     * @throws Exception
     */
    public static String sign(byte[] data, PrivateKey privateKey, String signAlgorithm) throws NoSuchAlgorithmException, InvalidKeyException, SignatureException {
        // 用私钥对信息生成数字签名
        Signature signature = Signature.getInstance(signAlgorithm);
        signature.initSign(privateKey);
        signature.update(data);
        return org.apache.commons.codec.binary.Base64.encodeBase64String(signature.sign());
    }

    /**
     * 校验数字签名
     *
     * @param data   加密数据
     * @param pubKey 公钥
     * @param sign   数字签名
     * @return 校验成功返回true，失败返回false
     * @throws Exception
     */
    public static boolean verify(byte[] data, PublicKey pubKey, String sign) throws NoSuchAlgorithmException, InvalidKeyException, SignatureException {
        return verify(data, pubKey, sign, SIGNATURE_ALGORITHM);
    }

    /**
     * 校验数字签名
     *
     * @param data   加密数据
     * @param pubKey 公钥
     * @param sign   数字签名
     * @return 校验成功返回true，失败返回false
     * @throws Exception
     */
    public static boolean verify(byte[] data, PublicKey pubKey, String sign, String signAlgorithm) throws NoSuchAlgorithmException, InvalidKeyException, SignatureException {
        Signature signature = Signature.getInstance(signAlgorithm);
        signature.initVerify(pubKey);
        signature.update(data);
        // 验证签名是否正常
        return signature.verify(org.apache.commons.codec.binary.Base64.decodeBase64(sign));
    }

    /**
     * 获取公钥证书
     *
     * @param pemContent 公钥证书Base64密文
     * @return 公钥证书
     * @throws Exception 解析异常
     */
    public static X509Certificate loadCertificate(String pemContent) throws CertificateException {
        CertificateFactory factory = null;
        factory = CertificateFactory.getInstance("X.509");
        ByteArrayInputStream is = new ByteArrayInputStream(Base64.getDecoder().decode(pemContent));
        X509Certificate certificate = (X509Certificate) factory.generateCertificate(is);
        return certificate;
    }

    /**
     * <P>
     * 私钥解密
     * </p>
     *
     * @param encryptedData  已加密数据
     * @param privateKey     私钥(BASE64编码)
     * @param transformation 算法/模式/填充 或 算法
     * @return
     * @throws Exception
     */
    public static byte[] decryptByPrivateKey(byte[] encryptedData,
                                             String privateKey, String transformation) throws Exception {
        byte[] keyBytes = Base64.getDecoder().decode(privateKey);
        PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes);
        KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
        RSAPrivateKey privateK = (RSAPrivateKey) keyFactory.generatePrivate(pkcs8KeySpec);
        Cipher cipher = Cipher.getInstance(transformation);
        cipher.init(Cipher.DECRYPT_MODE, privateK);
        return cipher.doFinal(encryptedData);
    }

    //===MODIFY BY Shanfq ON 2019-09-03 FOR MQC 31073 | REVIEW BY linpeng ON 2019-09-17===
    // 私钥加密
    public static byte[] encryptByPrivateKey(byte[] data, byte[] key)
            throws Exception {
        PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(key);
        KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
        PrivateKey privateKey = keyFactory.generatePrivate(pkcs8KeySpec);
        Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
        cipher.init(Cipher.ENCRYPT_MODE, privateKey);
        return cipher.doFinal(data);
    }
}